home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 June / PersonalComputerWorld-June2009-CoverdiscCD.iso / Software / Freeware / Firebug 1.3.3 / firebug-1.3.3-fx.xpi / content / firebug / dom.js < prev    next >
Encoding:
JavaScript  |  2009-02-19  |  41.9 KB  |  1,498 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. FBL.ns(function() { with (FBL) {
  4.  
  5. // ************************************************************************************************
  6. // Constants
  7.  
  8. const Cc = Components.classes;
  9. const Ci = Components.interfaces;
  10. const jsdIStackFrame = Ci.jsdIStackFrame;
  11.  
  12. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  13.  
  14. const insertSliceSize = 18;
  15. const insertInterval = 40;
  16.  
  17. const ignoreVars =
  18. {
  19.     "__firebug__": 1,
  20.     "eval": 1,
  21.  
  22.     // We are forced to ignore Java-related variables, because
  23.     // trying to access them causes browser freeze
  24.     "java": 1,
  25.     "sun": 1,
  26.     "Packages": 1,
  27.     "JavaArray": 1,
  28.     "JavaMember": 1,
  29.     "JavaObject": 1,
  30.     "JavaClass": 1,
  31.     "JavaPackage": 1,
  32.     "_firebug": 1,
  33.     "_FirebugConsole": 1,
  34.     "_FirebugCommandLine": 1,
  35. };
  36.  
  37. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  38.  
  39. const RowTag =
  40.     TR({class: "memberRow $member.open $member.type\\Row", $hasChildren: "$member.hasChildren",
  41.         level: "$member.level"},
  42.         TD({class: "memberLabelCell", style: "padding-left: $member.indent\\px"},
  43.             DIV({class: "memberLabel $member.type\\Label"}, "$member.name")
  44.         ),
  45.         TD({class: "memberValueCell"},
  46.             TAG("$member.tag", {object: "$member.value"})
  47.         )
  48.     );
  49.  
  50. const WatchRowTag =
  51.     TR({class: "watchNewRow", level: 0},
  52.         TD({class: "watchEditCell", colspan: 2},
  53.             DIV({class: "watchEditBox"},
  54.                     $STR("NewWatch")
  55.             )
  56.         )
  57.     );
  58.  
  59. const SizerRow =
  60.     TR(
  61.         TD({width: "30%"}),
  62.         TD({width: "70%"})
  63.     );
  64.  
  65. const DirTablePlate = domplate(Firebug.Rep,
  66. {
  67.     tag:
  68.         TABLE({class: "domTable", cellpadding: 0, cellspacing: 0, onclick: "$onClick"},
  69.             TBODY(
  70.                 SizerRow,
  71.                 FOR("member", "$object|memberIterator", RowTag)
  72.             )
  73.         ),
  74.  
  75.     watchTag:
  76.         TABLE({class: "domTable", cellpadding: 0, cellspacing: 0,
  77.                _toggles: "$toggles", _domPanel: "$domPanel", onclick: "$onClick"},
  78.             TBODY(
  79.                 SizerRow,
  80.                 WatchRowTag
  81.             )
  82.         ),
  83.  
  84.     tableTag:
  85.         TABLE({class: "domTable", cellpadding: 0, cellspacing: 0,
  86.             _toggles: "$toggles", _domPanel: "$domPanel", onclick: "$onClick"},
  87.             TBODY(
  88.                 SizerRow
  89.             )
  90.         ),
  91.  
  92.     rowTag:
  93.         FOR("member", "$members", RowTag),
  94.  
  95.     memberIterator: function(object, level)
  96.     {
  97.         return getMembers(object, level);
  98.     },
  99.  
  100.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  101.  
  102.     onClick: function(event)
  103.     {
  104.         if (!isLeftClick(event))
  105.             return;
  106.  
  107.         var row = getAncestorByClass(event.target, "memberRow");
  108.         var label = getAncestorByClass(event.target, "memberLabel");
  109.         if (label && hasClass(row, "hasChildren"))
  110.         {
  111.             var row = label.parentNode.parentNode;
  112.             this.toggleRow(row);
  113.         }
  114.         else
  115.         {
  116.             var object = Firebug.getRepObject(event.target);
  117.             if (typeof(object) == "function")
  118.             {
  119.                 FirebugChrome.select(object, "script");
  120.                 cancelEvent(event);
  121.             }
  122.             else if (event.detail == 2 && !object)
  123.             {
  124.                 var panel = row.parentNode.parentNode.domPanel;
  125.                 if (panel)
  126.                 {
  127.                     var rowValue = panel.getRowPropertyValue(row);
  128.                     if (typeof(rowValue) == "boolean")
  129.                         panel.setPropertyValue(row, !rowValue);
  130.                     else
  131.                         panel.editProperty(row);
  132.  
  133.                     cancelEvent(event);
  134.                 }
  135.             }
  136.         }
  137.     },
  138.  
  139.     toggleRow: function(row)
  140.     {
  141.         var level = parseInt(row.getAttribute("level"));
  142.         var toggles = row.parentNode.parentNode.toggles;
  143.  
  144.         if (hasClass(row, "opened"))
  145.         {
  146.             removeClass(row, "opened");
  147.  
  148.             if (toggles)
  149.             {
  150.                 var path = getPath(row);
  151.  
  152.                 // Remove the path from the toggle tree
  153.                 for (var i = 0; i < path.length; ++i)
  154.                 {
  155.                     if (i == path.length-1)
  156.                         delete toggles[path[i]];
  157.                     else
  158.                         toggles = toggles[path[i]];
  159.                 }
  160.             }
  161.  
  162.             var rowTag = this.rowTag;
  163.             var tbody = row.parentNode;
  164.  
  165.             setTimeout(function()
  166.             {
  167.                 for (var firstRow = row.nextSibling; firstRow; firstRow = row.nextSibling)
  168.                 {
  169.                     if (parseInt(firstRow.getAttribute("level")) <= level)
  170.                         break;
  171.  
  172.                     tbody.removeChild(firstRow);
  173.                 }
  174.             }, row.insertTimeout ? row.insertTimeout : 0);
  175.         }
  176.         else
  177.         {
  178.             setClass(row, "opened");
  179.  
  180.             if (toggles)
  181.             {
  182.                 var path = getPath(row);
  183.  
  184.                 // Mark the path in the toggle tree
  185.                 for (var i = 0; i < path.length; ++i)
  186.                 {
  187.                     var name = path[i];
  188.                     if (toggles.hasOwnProperty(name))
  189.                         toggles = toggles[name];
  190.                     else
  191.                         toggles = toggles[name] = {};
  192.                 }
  193.             }
  194.  
  195.             var value = row.lastChild.firstChild.repObject;
  196.             var members = getMembers(value, level+1);
  197.  
  198.             var rowTag = this.rowTag;
  199.             var lastRow = row;
  200.  
  201.             var delay = 0;
  202.             while (members.length)
  203.             {
  204.                 setTimeout(function(slice, isLast)
  205.                 {
  206.                     if (lastRow.parentNode)
  207.                         lastRow = rowTag.insertRows({members: slice}, lastRow)[1];
  208.  
  209.                     if (isLast)
  210.                         delete row.insertTimeout;
  211.                 }, delay, members.splice(0, insertSliceSize), !members.length);
  212.  
  213.                 delay += insertInterval;
  214.             }
  215.  
  216.             row.insertTimeout = delay;
  217.         }
  218.     }
  219. });
  220.  
  221. const ToolboxPlate = domplate(
  222. {
  223.     tag:
  224.         DIV({class: "watchToolbox", _domPanel: "$domPanel", onclick: "$onClick"},
  225.             IMG({class: "watchDeleteButton closeButton", src: "blank.gif"})
  226.         ),
  227.  
  228.     onClick: function(event)
  229.     {
  230.         var toolbox = event.currentTarget;
  231.         toolbox.domPanel.deleteWatch(toolbox.watchRow);
  232.     }
  233. });
  234.  
  235. // ************************************************************************************************
  236.  
  237. function DOMBasePanel() {}
  238.  
  239. DOMBasePanel.prototype = extend(Firebug.AblePanel,
  240. {
  241.     tag: DirTablePlate.tableTag,
  242.  
  243.     rebuild: function(update, scrollTop)
  244.     {
  245.         var members = getMembers(this.selection);
  246.         expandMembers(members, this.toggles, 0, 0);
  247.  
  248.         this.showMembers(members, update, scrollTop);
  249.     },
  250.  
  251.     showMembers: function(members, update, scrollTop)
  252.     {
  253.         // If we are still in the midst of inserting rows, cancel all pending
  254.         // insertions here - this is a big speedup when stepping in the debugger
  255.         if (this.timeouts)
  256.         {
  257.             for (var i = 0; i < this.timeouts.length; ++i)
  258.                 this.context.clearTimeout(this.timeouts[i]);
  259.             delete this.timeouts;
  260.         }
  261.  
  262.         if (!members.length)
  263.             return this.showEmptyMembers();
  264.  
  265.         var panelNode = this.panelNode;
  266.         var priorScrollTop = scrollTop == undefined ? panelNode.scrollTop : scrollTop;
  267.  
  268.         // If we are asked to "update" the current view, then build the new table
  269.         // offscreen and swap it in when it's done
  270.         var offscreen = update && panelNode.firstChild;
  271.         var dest = offscreen ? this.document : panelNode;
  272.  
  273.         var table = this.tag.replace({domPanel: this, toggles: this.toggles}, dest);
  274.         var tbody = table.lastChild;
  275.         var rowTag = DirTablePlate.rowTag;
  276.  
  277.         // Insert the first slice immediately
  278.         var slice = members.splice(0, insertSliceSize);
  279.         rowTag.insertRows({members: slice}, tbody.lastChild);
  280.  
  281.         var timeouts = [];
  282.  
  283.         var delay = 0;
  284.         while (members.length)
  285.         {
  286.             timeouts.push(this.context.setTimeout(function(slice)
  287.             {
  288.                 rowTag.insertRows({members: slice}, tbody.lastChild);
  289.  
  290.                 if ((panelNode.scrollHeight+panelNode.offsetHeight) >= priorScrollTop)
  291.                     panelNode.scrollTop = priorScrollTop;
  292.             }, delay, members.splice(0, insertSliceSize)));
  293.  
  294.             delay += insertInterval;
  295.         }
  296.  
  297.         if (offscreen)
  298.         {
  299.             timeouts.push(this.context.setTimeout(function()
  300.             {
  301.                 if (panelNode.firstChild)
  302.                     panelNode.replaceChild(table, panelNode.firstChild);
  303.                 else
  304.                     panelNode.appendChild(table);
  305.  
  306.                 // Scroll back to where we were before
  307.                 panelNode.scrollTop = priorScrollTop;
  308.             }, delay));
  309.         }
  310.         else
  311.         {
  312.             timeouts.push(this.context.setTimeout(function()
  313.             {
  314.                 panelNode.scrollTop = scrollTop == undefined ? 0 : scrollTop;
  315.             }, delay));
  316.         }
  317.  
  318.         this.timeouts = [];
  319.     },
  320.  
  321.     showEmptyMembers: function()
  322.     {
  323.         FirebugReps.Warning.tag.replace({object: "NoMembersWarning"}, this.panelNode);
  324.     },
  325.  
  326.     findPathObject: function(object)
  327.     {
  328.         var pathIndex = -1;
  329.         for (var i = 0; i < this.objectPath.length; ++i)
  330.         {
  331.             if (this.getPathObject(i) == object)
  332.                 return i;
  333.         }
  334.  
  335.         return -1;
  336.     },
  337.  
  338.     getPathObject: function(index)
  339.     {
  340.         var object = this.objectPath[index];
  341.         if (object instanceof Property)
  342.             return object.getObject();
  343.         else
  344.             return object;
  345.     },
  346.  
  347.     getRowObject: function(row)
  348.     {
  349.         var object = getRowOwnerObject(row);
  350.         return object ? object : this.selection;
  351.     },
  352.  
  353.     getRowPropertyValue: function(row)
  354.     {
  355.         var object = this.getRowObject(row);
  356.         if (object)
  357.         {
  358.             var propName = getRowName(row);
  359.  
  360.             if (object instanceof jsdIStackFrame)
  361.                 return Firebug.Debugger.evaluate(propName, this.context);
  362.             else
  363.                 return object[propName];
  364.         }
  365.     },
  366.  
  367.     copyProperty: function(row)
  368.     {
  369.         var value = this.getRowPropertyValue(row);
  370.         copyToClipboard(value);
  371.     },
  372.  
  373.     editProperty: function(row, editValue)
  374.     {
  375.         if (hasClass(row, "watchNewRow"))
  376.         {
  377.             if (Firebug.Console.isEnabled(this.context))
  378.                 Firebug.Editor.startEditing(row, "");
  379.         }
  380.         else if (hasClass(row, "watchRow"))
  381.             Firebug.Editor.startEditing(row, getRowName(row));
  382.         else
  383.         {
  384.             var object = this.getRowObject(row);
  385.             this.context.thisValue = object;
  386.  
  387.             if (!editValue)
  388.             {
  389.                 var propValue = this.getRowPropertyValue(row);
  390.  
  391.                 var type = typeof(propValue);
  392.                 if (type == "undefined" || type == "number" || type == "boolean")
  393.                     editValue = propValue;
  394.                 else if (type == "string")
  395.                     editValue = "\"" + escapeJS(propValue) + "\"";
  396.                 else if (propValue == null)
  397.                     editValue = "null";
  398.                 else if (object instanceof Window || object instanceof jsdIStackFrame)
  399.                     editValue = getRowName(row);
  400.                 else
  401.                     editValue = "this." + getRowName(row);
  402.             }
  403.  
  404.  
  405.             Firebug.Editor.startEditing(row, editValue);
  406.         }
  407.     },
  408.  
  409.     deleteProperty: function(row)
  410.     {
  411.         if (hasClass(row, "watchRow"))
  412.             this.deleteWatch(row);
  413.         else
  414.         {
  415.             var object = getRowOwnerObject(row);
  416.             if (!object)
  417.                 object = this.selection;
  418.  
  419.             if (object)
  420.             {
  421.                 var name = getRowName(row);
  422.                 try
  423.                 {
  424.                     delete object[name];
  425.                 }
  426.                 catch (exc)
  427.                 {
  428.                     return;
  429.                 }
  430.  
  431.                 this.rebuild(true);
  432.                 this.markChange();
  433.             }
  434.         }
  435.     },
  436.  
  437.     setPropertyValue: function(row, value)  // value must be string
  438.     {
  439.         var name = getRowName(row);
  440.         if (name == "this")
  441.             return;
  442.  
  443.         var object = this.getRowObject(row);
  444.         if (object && !(object instanceof jsdIStackFrame))
  445.         {
  446.              // unwrappedJSObject.property = unwrappedJSObject
  447.              Firebug.CommandLine.evaluate(value, this.context, object, this.context.window,
  448.                  function success(result, context)
  449.                  {
  450.                      object[name] = result;
  451.                  },
  452.                  function failed(result, context)
  453.                  {
  454.                      try
  455.                      {
  456.                          // If the value doesn't parse, then just store it as a string.  Some users will
  457.                          // not realize they're supposed to enter a JavaScript expression and just type
  458.                          // literal text
  459.                          object[name] = String(value);  // unwrappedJSobject.property = string
  460.                      }
  461.                      catch (exc)
  462.                      {
  463.                          return;
  464.                      }
  465.                   }
  466.              );
  467.         }
  468.         else if (this.context.stopped)
  469.         {
  470.             try
  471.             {
  472.                 Firebug.CommandLine.evaluate(name+"="+value, this.context);
  473.             }
  474.             catch (exc)
  475.             {
  476.                 try
  477.                 {
  478.                     // See catch block above...
  479.                     object[name] = String(value); // unwrappedJSobject.property = string
  480.                 }
  481.                 catch (exc)
  482.                 {
  483.                     return;
  484.                 }
  485.             }
  486.         }
  487.  
  488.         this.rebuild(true);
  489.         this.markChange();
  490.     },
  491.  
  492.     highlightRow: function(row)
  493.     {
  494.         if (this.highlightedRow)
  495.             cancelClassTimed(this.highlightedRow, "jumpHighlight", this.context);
  496.  
  497.         this.highlightedRow = row;
  498.  
  499.         if (row)
  500.             setClassTimed(row, "jumpHighlight", this.context);
  501.     },
  502.  
  503.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  504.     // extends Panel
  505.  
  506.     initialize: function()
  507.     {
  508.         this.objectPath = [];
  509.         this.propertyPath = [];
  510.         this.viewPath = [];
  511.         this.pathIndex = -1;
  512.         this.toggles = {};
  513.  
  514.         Firebug.Panel.initialize.apply(this, arguments);
  515.     },
  516.  
  517.     destroy: function(state)
  518.     {
  519.         var view = this.viewPath[this.pathIndex];
  520.         if (view && this.panelNode.scrollTop)
  521.             view.scrollTop = this.panelNode.scrollTop;
  522.  
  523.         state.pathIndex = this.pathIndex;
  524.         state.viewPath = this.viewPath;
  525.         state.propertyPath = this.propertyPath;
  526.         if (this.propertyPath.length > 0 && !this.propertyPath[1])
  527.             state.firstSelection = persistObject(this.getPathObject(1), this.context);
  528.  
  529.         Firebug.Panel.destroy.apply(this, arguments);
  530.     },
  531.  
  532.     show: function(state)
  533.     {
  534.         if (this.context.loaded && !this.selection)
  535.         {
  536.             if (!state)
  537.             {
  538.                 this.select(null);
  539.                 return;
  540.             }
  541.  
  542.             this.viewPath = state.viewPath;
  543.             this.propertyPath = state.propertyPath;
  544.  
  545.             var selectObject = defaultObject = this.getDefaultSelection(this.context);
  546.  
  547.             if (state.firstSelection)
  548.             {
  549.                 var restored = state.firstSelection(this.context);
  550.                 if (restored)
  551.                 {
  552.                     selectObject = restored;
  553.                     this.objectPath = [defaultObject, restored];
  554.                 }
  555.                 else
  556.                     this.objectPath = [defaultObject];
  557.             }
  558.             else
  559.                 this.objectPath = [defaultObject];
  560.  
  561.             if (this.propertyPath.length > 1)
  562.             {
  563.                 for (var i = 1; i < this.propertyPath.length; ++i)
  564.                 {
  565.                     var name = this.propertyPath[i];
  566.                     if (!name)
  567.                         continue;
  568.  
  569.                     var object = selectObject;
  570.                     try
  571.                     {
  572.                         selectObject = object[name];
  573.                     }
  574.                     catch (exc)
  575.                     {
  576.                         selectObject = null;
  577.                     }
  578.  
  579.                     if (selectObject)
  580.                     {
  581.                         this.objectPath.push(new Property(object, name));
  582.                     }
  583.                     else
  584.                     {
  585.                         // If we can't access a property, just stop
  586.                         this.viewPath.splice(i);
  587.                         this.propertyPath.splice(i);
  588.                         this.objectPath.splice(i);
  589.                         selectObject = this.getPathObject(this.objectPath.length-1);
  590.                         break;
  591.                     }
  592.                 }
  593.             }
  594.  
  595.             var selection = state.pathIndex <= this.objectPath.length-1
  596.                 ? this.getPathObject(state.pathIndex)
  597.                 : this.getPathObject(this.objectPath.length-1);
  598.  
  599.             this.select(selection);
  600.         }
  601.     },
  602.  
  603.     hide: function()
  604.     {
  605.         var view = this.viewPath[this.pathIndex];
  606.         if (view && this.panelNode.scrollTop)
  607.             view.scrollTop = this.panelNode.scrollTop;
  608.     },
  609.  
  610.     supportsObject: function(object)
  611.     {
  612.         if (object == null)
  613.             return 1000;
  614.  
  615.         if (typeof(object) == "undefined")
  616.             return 1000;
  617.         else if (object instanceof SourceLink)
  618.             return 0;
  619.         else
  620.             return 1; // just agree to support everything but not agressively.
  621.     },
  622.  
  623.     refresh: function()
  624.     {
  625.         this.rebuild(true);
  626.     },
  627.  
  628.     updateSelection: function(object)
  629.     {
  630.         var previousIndex = this.pathIndex;
  631.         var previousView = previousIndex == -1 ? null : this.viewPath[previousIndex];
  632.  
  633.         var newPath = this.pathToAppend;
  634.         delete this.pathToAppend;
  635.  
  636.         var pathIndex = this.findPathObject(object);
  637.         if (newPath || pathIndex == -1)
  638.         {
  639.             this.toggles = {};
  640.  
  641.             if (newPath)
  642.             {
  643.                 // Remove everything after the point where we are inserting, so we
  644.                 // essentially replace it with the new path
  645.                 if (previousView)
  646.                 {
  647.                     if (this.panelNode.scrollTop)
  648.                         previousView.scrollTop = this.panelNode.scrollTop;
  649.  
  650.                     this.objectPath.splice(previousIndex+1);
  651.                     this.propertyPath.splice(previousIndex+1);
  652.                     this.viewPath.splice(previousIndex+1);
  653.                 }
  654.  
  655.                 var value = this.getPathObject(previousIndex);
  656.                 if (!value)
  657.                 {
  658.                     return;
  659.                 }
  660.  
  661.                 for (var i = 0; i < newPath.length; ++i)
  662.                 {
  663.                     var name = newPath[i];
  664.                     var object = value;
  665.                     value = value[name];
  666.  
  667.                     ++this.pathIndex;
  668.                     this.objectPath.push(new Property(object, name));
  669.                     this.propertyPath.push(name);
  670.                     this.viewPath.push({toggles: this.toggles, scrollTop: 0});
  671.                 }
  672.             }
  673.             else
  674.             {
  675.                 this.toggles = {};
  676.  
  677.                 var win = this.context.window;
  678.                 if (object == win)
  679.                 {
  680.                     this.pathIndex = 0;
  681.                     this.objectPath = [win];
  682.                     this.propertyPath = [null];
  683.                     this.viewPath = [{toggles: this.toggles, scrollTop: 0}];
  684.                 }
  685.                 else
  686.                 {
  687.                     this.pathIndex = 1;
  688.                     this.objectPath = [win, object];
  689.                     this.propertyPath = [null, null];
  690.                     this.viewPath = [
  691.                         {toggles: {}, scrollTop: 0},
  692.                         {toggles: this.toggles, scrollTop: 0}
  693.                     ];
  694.                 }
  695.             }
  696.  
  697.             this.panelNode.scrollTop = 0;
  698.             this.rebuild();
  699.         }
  700.         else
  701.         {
  702.             this.pathIndex = pathIndex;
  703.  
  704.             var view = this.viewPath[pathIndex];
  705.             this.toggles = view.toggles;
  706.  
  707.             // Persist the current scroll location
  708.             if (previousView && this.panelNode.scrollTop)
  709.                 previousView.scrollTop = this.panelNode.scrollTop;
  710.  
  711.             this.rebuild(false, view.scrollTop);
  712.         }
  713.  
  714.     },
  715.  
  716.     getObjectPath: function(object)
  717.     {
  718.         return this.objectPath;
  719.     },
  720.  
  721.     getDefaultSelection: function()
  722.     {
  723.         return this.context.window;
  724.     },
  725.  
  726.     updateOption: function(name, value)
  727.     {
  728.         const optionMap = {showUserProps: 1, showUserFuncs: 1, showDOMProps: 1,
  729.             showDOMFuncs: 1, showDOMConstants: 1};
  730.         if ( optionMap.hasOwnProperty(name) )
  731.             this.rebuild(true);
  732.     },
  733.  
  734.     getOptionsMenuItems: function()
  735.     {
  736.         return [
  737.             optionMenu("ShowUserProps", "showUserProps"),
  738.             optionMenu("ShowUserFuncs", "showUserFuncs"),
  739.             optionMenu("ShowDOMProps", "showDOMProps"),
  740.             optionMenu("ShowDOMFuncs", "showDOMFuncs"),
  741.             optionMenu("ShowDOMConstants", "showDOMConstants"),
  742.             "-",
  743.             {label: "Refresh", command: bindFixed(this.rebuild, this, true) }
  744.         ];
  745.     },
  746.  
  747.     getContextMenuItems: function(object, target)
  748.     {
  749.         var row = getAncestorByClass(target, "memberRow");
  750.  
  751.         var items = [];
  752.  
  753.         if (row)
  754.         {
  755.             var rowName = getRowName(row);
  756.             var rowObject = this.getRowObject(row);
  757.             var rowValue = this.getRowPropertyValue(row);
  758.  
  759.             var isWatch = hasClass(row, "watchRow");
  760.             var isStackFrame = rowObject instanceof jsdIStackFrame;
  761.  
  762.             if (typeof(rowValue) == "string" || typeof(rowValue) == "number")
  763.             {
  764.                 // Functions already have a copy item in their context menu
  765.                 items.push(
  766.                     "-",
  767.                     {label: "CopyValue",
  768.                         command: bindFixed(this.copyProperty, this, row) }
  769.                 );
  770.             }
  771.  
  772.             items.push(
  773.                 "-",
  774.                 {label: isWatch ? "EditWatch" : (isStackFrame ? "EditVariable" : "EditProperty"),
  775.                     command: bindFixed(this.editProperty, this, row) }
  776.             );
  777.  
  778.             if (isWatch || (!isStackFrame && !isDOMMember(rowObject, rowName)))
  779.             {
  780.                 items.push(
  781.                     {label: isWatch ? "DeleteWatch" : "DeleteProperty",
  782.                         command: bindFixed(this.deleteProperty, this, row) }
  783.                 );
  784.             }
  785.         }
  786.  
  787.         items.push(
  788.             "-",
  789.             {label: "Refresh", command: bindFixed(this.rebuild, this, true) }
  790.         );
  791.  
  792.         return items;
  793.     },
  794.  
  795.     getEditor: function(target, value)
  796.     {
  797.         if (!this.editor)
  798.             this.editor = new DOMEditor(this.document);
  799.  
  800.         return this.editor;
  801.     }
  802. });
  803.  
  804. // ************************************************************************************************
  805.  
  806. var DOMMainPanel = Firebug.DOMPanel = function () {};
  807.  
  808. Firebug.DOMPanel.DirTable = DirTablePlate;
  809.  
  810. DOMMainPanel.prototype = extend(DOMBasePanel.prototype,
  811. {
  812.     selectRow: function(row, target)
  813.     {
  814.         if (!target)
  815.             target = row.lastChild.firstChild;
  816.  
  817.         if (!target || !target.repObject)
  818.             return;
  819.  
  820.         this.pathToAppend = getPath(row);
  821.  
  822.         // If the object is inside an array, look up its index
  823.         var valueBox = row.lastChild.firstChild;
  824.         if (hasClass(valueBox, "objectBox-array"))
  825.         {
  826.             var arrayIndex = FirebugReps.Arr.getItemIndex(target);
  827.             this.pathToAppend.push(arrayIndex);
  828.         }
  829.  
  830.         // Make sure we get a fresh status path for the object, since otherwise
  831.         // it might find the object in the existing path and not refresh it
  832.         this.context.chrome.clearStatusPath();
  833.  
  834.         this.select(target.repObject, true);
  835.     },
  836.  
  837.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  838.  
  839.     onClick: function(event)
  840.     {
  841.         var repNode = Firebug.getRepNode(event.target);
  842.         if (repNode)
  843.         {
  844.             var row = getAncestorByClass(event.target, "memberRow");
  845.             if (row)
  846.             {
  847.                 this.selectRow(row, repNode);
  848.                 cancelEvent(event);
  849.             }
  850.         }
  851.     },
  852.  
  853.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  854.     // extends Panel
  855.  
  856.     name: "dom",
  857.     searchable: true,
  858.     statusSeparator: ">",
  859.  
  860.     initialize: function()
  861.     {
  862.         this.onClick = bind(this.onClick, this);
  863.  
  864.         DOMBasePanel.prototype.initialize.apply(this, arguments);
  865.     },
  866.  
  867.     initializeNode: function(oldPanelNode)
  868.     {
  869.         this.panelNode.addEventListener("click", this.onClick, false);
  870.     },
  871.  
  872.     destroyNode: function()
  873.     {
  874.         this.panelNode.removeEventListener("click", this.onClick, false);
  875.     },
  876.  
  877.     search: function(text, visit)
  878.     {
  879.         if (!text)
  880.         {
  881.             delete this.currentSearch;
  882.             this.highlightRow(null);
  883.             return false;
  884.         }
  885.  
  886.         if (visit && this.currentSearch && this.currentSearch.currentNode)
  887.         {
  888.             Firebug.Search.clear(this.context);
  889.             this.selectRow(this.currentSearch.currentNode);
  890.             delete this.currentSearch;
  891.             return true;
  892.         }
  893.         else
  894.         {
  895.             var row;
  896.             if (this.currentSearch && text == this.currentSearch.text)
  897.                 row = this.currentSearch.findNext(true);
  898.             else
  899.             {
  900.                 function findRow(node) { return getAncestorByClass(node, "memberRow"); }
  901.                 this.currentSearch = new TextSearch(this.panelNode, findRow);
  902.                 row = this.currentSearch.find(text);
  903.             }
  904.  
  905.             if (row)
  906.             {
  907.                 if (visit)
  908.                 {
  909.                     Firebug.Search.clear(this.context);
  910.                     this.selectRow(row);
  911.                     delete this.currentSearch;
  912.                 }
  913.                 else
  914.                 {
  915.                     var sel = this.document.defaultView.getSelection();
  916.                     sel.removeAllRanges();
  917.                     sel.addRange(this.currentSearch.range);
  918.  
  919.                     scrollIntoCenterView(row, this.panelNode);
  920.  
  921.                     this.highlightRow(row);
  922.                 }
  923.                 return true;
  924.             }
  925.             else
  926.                 return false;
  927.         }
  928.     }
  929. });
  930.  
  931. // ************************************************************************************************
  932.  
  933. function DOMSidePanel() {}
  934.  
  935. DOMSidePanel.prototype = extend(DOMBasePanel.prototype,
  936. {
  937.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  938.     // extends Panel
  939.  
  940.     name: "domSide",
  941.     parentPanel: "html",
  942.     order: 3,
  943. });
  944.  
  945. // ************************************************************************************************
  946.  
  947. function WatchPanel() {}
  948.  
  949. WatchPanel.prototype = extend(DOMBasePanel.prototype,
  950. {
  951.     tag: DirTablePlate.watchTag,
  952.  
  953.     rebuild: function()
  954.     {
  955.         this.updateSelection(this.selection);
  956.     },
  957.  
  958.     showEmptyMembers: function()
  959.     {
  960.         this.tag.replace({domPanel: this, toggles: {}}, this.panelNode);
  961.     },
  962.  
  963.     addWatch: function(expression)
  964.     {
  965.         if (!this.watches)
  966.             this.watches = [];
  967.  
  968.         this.watches.splice(0, 0, expression);
  969.         this.rebuild(true);
  970.     },
  971.  
  972.     removeWatch: function(expression)
  973.     {
  974.         if (!this.watches)
  975.             return;
  976.  
  977.         var index = this.watches.indexOf(expression);
  978.         if (index != -1)
  979.             this.watches.splice(index, 1);
  980.     },
  981.  
  982.     editNewWatch: function(value)
  983.     {
  984.         var watchNewRow = getElementByClass(this.panelNode, "watchNewRow");
  985.         if (watchNewRow)
  986.             this.editProperty(watchNewRow, value);
  987.     },
  988.  
  989.     setWatchValue: function(row, value)
  990.     {
  991.         var rowIndex = getWatchRowIndex(row);
  992.         this.watches[rowIndex] = value;
  993.         this.rebuild(true);
  994.     },
  995.  
  996.     deleteWatch: function(row)
  997.     {
  998.         var rowIndex = getWatchRowIndex(row);
  999.         this.watches.splice(rowIndex, 1);
  1000.         this.rebuild(true);
  1001.  
  1002.         this.context.setTimeout(bindFixed(function()
  1003.         {
  1004.             this.showToolbox(null);
  1005.         }, this));
  1006.     },
  1007.  
  1008.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1009.  
  1010.     showToolbox: function(row)
  1011.     {
  1012.         var toolbox = this.getToolbox();
  1013.         if (row)
  1014.         {
  1015.             if (hasClass(row, "editing"))
  1016.                 return;
  1017.  
  1018.             toolbox.watchRow = row;
  1019.  
  1020.             var offset = getClientOffset(row);
  1021.             toolbox.style.top = offset.y + "px";
  1022.             this.panelNode.appendChild(toolbox);
  1023.         }
  1024.         else
  1025.         {
  1026.             delete toolbox.watchRow;
  1027.             if (toolbox.parentNode)
  1028.                 toolbox.parentNode.removeChild(toolbox);
  1029.         }
  1030.     },
  1031.  
  1032.     getToolbox: function()
  1033.     {
  1034.         if (!this.toolbox)
  1035.             this.toolbox = ToolboxPlate.tag.replace({domPanel: this}, this.document);
  1036.  
  1037.         return this.toolbox;
  1038.     },
  1039.  
  1040.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1041.  
  1042.     onMouseDown: function(event)
  1043.     {
  1044.         var watchNewRow = getAncestorByClass(event.target, "watchNewRow");
  1045.         if (watchNewRow)
  1046.         {
  1047.             this.editProperty(watchNewRow);
  1048.             cancelEvent(event);
  1049.         }
  1050.     },
  1051.  
  1052.     onMouseOver: function(event)
  1053.     {
  1054.         var watchRow = getAncestorByClass(event.target, "watchRow");
  1055.         if (watchRow)
  1056.             this.showToolbox(watchRow);
  1057.     },
  1058.  
  1059.     onMouseOut: function(event)
  1060.     {
  1061.         if (isAncestor(event.relatedTarget, this.getToolbox()))
  1062.             return;
  1063.  
  1064.         var watchRow = getAncestorByClass(event.relatedTarget, "watchRow");
  1065.         if (!watchRow)
  1066.             this.showToolbox(null);
  1067.     },
  1068.  
  1069.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1070.     // extends Panel
  1071.  
  1072.     name: "watches",
  1073.     order: 0,
  1074.     parentPanel: "script",
  1075.  
  1076.     initialize: function()
  1077.     {
  1078.         this.onMouseDown = bind(this.onMouseDown, this);
  1079.         this.onMouseOver = bind(this.onMouseOver, this);
  1080.         this.onMouseOut = bind(this.onMouseOut, this);
  1081.  
  1082.         DOMBasePanel.prototype.initialize.apply(this, arguments);
  1083.     },
  1084.  
  1085.     destroy: function(state)
  1086.     {
  1087.         state.watches = this.watches;
  1088.  
  1089.         Firebug.Panel.destroy.apply(this, arguments);
  1090.     },
  1091.  
  1092.     show: function(state)
  1093.     {
  1094.         if (state && state.watches)
  1095.             this.watches = state.watches;
  1096.     },
  1097.  
  1098.     initializeNode: function(oldPanelNode)
  1099.     {
  1100.         this.panelNode.addEventListener("mousedown", this.onMouseDown, false);
  1101.         this.panelNode.addEventListener("mouseover", this.onMouseOver, false);
  1102.         this.panelNode.addEventListener("mouseout", this.onMouseOut, false);
  1103.     },
  1104.  
  1105.     destroyNode: function()
  1106.     {
  1107.         this.panelNode.removeEventListener("mousedown", this.onMouseDown, false);
  1108.         this.panelNode.removeEventListener("mouseover", this.onMouseOver, false);
  1109.         this.panelNode.removeEventListener("mouseout", this.onMouseOut, false);
  1110.     },
  1111.  
  1112.     refresh: function()
  1113.     {
  1114.         this.rebuild(true);
  1115.     },
  1116.  
  1117.     updateSelection: function(object)
  1118.     {
  1119.         var frame = this.context.currentFrame;
  1120.  
  1121.         var newFrame = frame && frame.isValid && frame.script != this.lastScript;
  1122.         if (newFrame)
  1123.         {
  1124.             this.toggles = {};
  1125.             this.lastScript = frame.script;
  1126.         }
  1127.  
  1128.         var members = [];
  1129.  
  1130.         var showWatches = this.context.onLoadWindowContent || this.context.stopped;
  1131.         if (this.watches && showWatches)
  1132.         {
  1133.             for (var i = 0; i < this.watches.length; ++i)
  1134.             {
  1135.                 var expr = this.watches[i];
  1136.                 var value = null;
  1137.                 Firebug.CommandLine.evaluate(expr, this.context, null, this.context.window,
  1138.                     function success(result, context)
  1139.                     {
  1140.                         value = result;
  1141.                     },
  1142.                     function failed(result, context)
  1143.                     {
  1144.                         var exc = result;
  1145.                         value = new ErrorCopy(exc+"");
  1146.                     }
  1147.                 );
  1148.  
  1149.                 addMember("watch", members, expr, value, 0);
  1150.             }
  1151.         }
  1152.  
  1153.         if (frame && frame.isValid)
  1154.         {
  1155.             var thisVar = frame.thisValue.getWrappedValue();
  1156.             addMember("user", members, "this", thisVar, 0);
  1157.  
  1158.             var scopeChain = this.generateScopeChain(frame.scope);
  1159.             addMember("scopes", members, "scopeChain", scopeChain, 0);
  1160.  
  1161.             members.push.apply(members, getMembers(scopeChain[0]));
  1162.         }
  1163.  
  1164.         expandMembers(members, this.toggles, 0, 0);
  1165.         this.showMembers(members, !newFrame);
  1166.     },
  1167.  
  1168.     generateScopeChain: function (scope) {
  1169.         var ret = [];
  1170.         while (scope) {
  1171.             var scopeVars;
  1172.             // getWrappedValue will not contain any variables for closure
  1173.             // scopes, so we want to special case this to get all variables
  1174.             // in all cases.
  1175.             if (scope.jsClassName == "Call") {
  1176.                 scopeVars = {};
  1177.                 var listValue = {value: null}, lengthValue = {value: 0};
  1178.                 scope.getProperties(listValue, lengthValue);
  1179.  
  1180.                 for (var i = 0; i < lengthValue.value; ++i)
  1181.                 {
  1182.                     var prop = listValue.value[i];
  1183.                     var name = prop.name.getWrappedValue();
  1184.                     if (ignoreVars[name] == 1)
  1185.                         continue;
  1186.  
  1187.                     scopeVars[name] = prop.value.getWrappedValue();
  1188.                 }
  1189.             } else {
  1190.                 scopeVars = scope.getWrappedValue();
  1191.             }
  1192.  
  1193.             if (!scopeVars.hasOwnProperty("toString")) {
  1194.                 (function() {
  1195.                     var className = scope.jsClassName;
  1196.                     scopeVars.toString = function() {
  1197.                         return $STR(className + " Scope");
  1198.                     };
  1199.                 })();
  1200.             }
  1201.  
  1202.             ret.push(scopeVars);
  1203.  
  1204.             scope = scope.jsParent;
  1205.         }
  1206.  
  1207.         ret.toString = function() {
  1208.             return $STR("Scope Chain");
  1209.         };
  1210.  
  1211.         return ret;
  1212.     },
  1213.     
  1214. });
  1215.  
  1216. // ************************************************************************************************
  1217. // Local Helpers
  1218.  
  1219. function DOMEditor(doc)
  1220. {
  1221.     this.box = this.tag.replace({}, doc, this);
  1222.     this.input = this.box;
  1223.  
  1224.     this.tabNavigation = false;
  1225.     this.tabCompletion = true;
  1226.     this.completeAsYouType = false;
  1227.     this.fixedWidth = true;
  1228.  
  1229.     this.autoCompleter = Firebug.CommandLine.autoCompleter;
  1230. }
  1231.  
  1232. DOMEditor.prototype = domplate(Firebug.InlineEditor.prototype,
  1233. {
  1234.     tag: INPUT({class: "fixedWidthEditor", type: "text",
  1235.                 oninput: "$onInput", onkeypress: "$onKeyPress"}),
  1236.  
  1237.     endEditing: function(target, value, cancel)
  1238.     {
  1239.         // XXXjoe Kind of hackish - fix me
  1240.         delete this.panel.context.thisValue;
  1241.  
  1242.         if (cancel || value == "")
  1243.             return;
  1244.  
  1245.         var row = getAncestorByClass(target, "memberRow");
  1246.         if (!row)
  1247.             this.panel.addWatch(value);
  1248.         else if (hasClass(row, "watchRow"))
  1249.             this.panel.setWatchValue(row, value);
  1250.         else
  1251.             this.panel.setPropertyValue(row, value);
  1252.     }
  1253. });
  1254.  
  1255. // ************************************************************************************************
  1256. // Local Helpers
  1257.  
  1258. function getMembers(object, level)  // we expect object to be user-level object wrapped in security blanket
  1259. {
  1260.     if (!level)
  1261.         level = 0;
  1262.  
  1263.     var ordinals = [], userProps = [], userClasses = [], userFuncs = [],
  1264.         domProps = [], domFuncs = [], domConstants = [];
  1265.  
  1266.     try
  1267.     {
  1268.         var domMembers = getDOMMembers(object);
  1269.  
  1270.         if (object.wrappedJSObject)
  1271.             var insecureObject = object.wrappedJSObject;
  1272.         else
  1273.             var insecureObject = object;
  1274.  
  1275.         for (var name in insecureObject)  // enumeration is safe
  1276.         {
  1277.             if (ignoreVars[name] == 1)  // javascript.options.strict says ignoreVars is undefined.
  1278.                 continue;
  1279.  
  1280.             var val;
  1281.             try
  1282.             {
  1283.                 val = insecureObject[name];  // getter is safe
  1284.             }
  1285.             catch (exc)
  1286.             {
  1287.                 // Sometimes we get exceptions trying to access certain members
  1288.             }
  1289.  
  1290.             var ordinal = parseInt(name);
  1291.             if (ordinal || ordinal == 0)
  1292.             {
  1293.                 addMember("ordinal", ordinals, name, val, level);
  1294.             }
  1295.             else if (typeof(val) == "function")
  1296.             {
  1297.                 if (isClassFunction(val))
  1298.                     addMember("userClass", userClasses, name, val, level);
  1299.                 else if (name in domMembers)
  1300.                     addMember("domFunction", domFuncs, name, val, level, domMembers[name]);
  1301.                 else
  1302.                     addMember("userFunction", userFuncs, name, val, level);
  1303.             }
  1304.             else
  1305.             {
  1306.                 if (name in domMembers)
  1307.                     addMember("dom", domProps, name, val, level, domMembers[name]);
  1308.                 else if (name in domConstantMap)
  1309.                     addMember("dom", domConstants, name, val, level);
  1310.                 else
  1311.                     addMember("user", userProps, name, val, level);
  1312.             }
  1313.         }
  1314.     }
  1315.     catch (exc)
  1316.     {
  1317.         // Sometimes we get exceptions just from trying to iterate the members
  1318.         // of certain objects, like StorageList, but don't let that gum up the works
  1319.         //throw exc;
  1320.     }
  1321.  
  1322.     function sortName(a, b) { return a.name > b.name ? 1 : -1; }
  1323.     function sortOrder(a, b) { return a.order > b.order ? 1 : -1; }
  1324.  
  1325.     var members = [];
  1326.  
  1327.     members.push.apply(members, ordinals);
  1328.  
  1329.     if (Firebug.showUserProps)
  1330.     {
  1331.         userProps.sort(sortName);
  1332.         members.push.apply(members, userProps);
  1333.     }
  1334.  
  1335.     if (Firebug.showUserFuncs)
  1336.     {
  1337.         userClasses.sort(sortName);
  1338.         members.push.apply(members, userClasses);
  1339.  
  1340.         userFuncs.sort(sortName);
  1341.         members.push.apply(members, userFuncs);
  1342.     }
  1343.  
  1344.     if (Firebug.showDOMProps)
  1345.     {
  1346.         domProps.sort(sortName);
  1347.         members.push.apply(members, domProps);
  1348.     }
  1349.  
  1350.     if (Firebug.showDOMFuncs)
  1351.     {
  1352.         domFuncs.sort(sortName);
  1353.         members.push.apply(members, domFuncs);
  1354.     }
  1355.  
  1356.     if (Firebug.showDOMConstants)
  1357.         members.push.apply(members, domConstants);
  1358.  
  1359.     return members;
  1360. }
  1361.  
  1362. function expandMembers(members, toggles, offset, level)  // recursion starts with offset=0, level=0
  1363. {
  1364.     var expanded = 0;
  1365.     for (var i = offset; i < members.length; ++i)
  1366.     {
  1367.         var member = members[i];
  1368.         if (member.level > level)
  1369.             break;
  1370.  
  1371.         if ( toggles.hasOwnProperty(member.name) )
  1372.         {
  1373.             member.open = "opened";  // member.level <= level && member.name in toggles.
  1374.  
  1375.             var newMembers = getMembers(member.value, level+1);  // sets newMembers.level to level+1
  1376.  
  1377.             var args = [i+1, 0];
  1378.             args.push.apply(args, newMembers);
  1379.             members.splice.apply(members, args);
  1380.             expanded += newMembers.length;
  1381.             i += newMembers.length + expandMembers(members, toggles[member.name], i+1, level+1);
  1382.         }
  1383.     }
  1384.  
  1385.     return expanded;
  1386. }
  1387.  
  1388. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1389.  
  1390. function isClassFunction(fn)
  1391. {
  1392.     try
  1393.     {
  1394.         for (var name in fn.prototype)
  1395.             return true;
  1396.     } catch (exc) {}
  1397.     return false;
  1398. }
  1399.  
  1400. function hasProperties(ob)
  1401. {
  1402.     try
  1403.     {
  1404.         for (var name in ob)
  1405.             return true;
  1406.     } catch (exc) {}
  1407.     return false;
  1408. }
  1409.  
  1410. function addMember(type, props, name, value, level, order)
  1411. {
  1412.     var rep = Firebug.getRep(value);    // do this first in case a call to instanceof reveals contents
  1413.     var tag = rep.shortTag ? rep.shortTag : rep.tag;
  1414.  
  1415.     var valueType = typeof(value);
  1416.     var hasChildren = hasProperties(value) && !(value instanceof ErrorCopy) &&
  1417.         (valueType == "function" || (valueType == "object" && value != null)
  1418.         || (valueType == "string" && value.length > Firebug.stringCropLength));
  1419.  
  1420.     props.push({
  1421.         name: name,
  1422.         value: value,
  1423.         type: type,
  1424.         rowClass: "memberRow-"+type,
  1425.         open: "",
  1426.         order: order,
  1427.         level: level,
  1428.         indent: level*16,
  1429.         hasChildren: hasChildren,
  1430.         tag: tag
  1431.     });
  1432. }
  1433.  
  1434. function getWatchRowIndex(row)
  1435. {
  1436.     var index = -1;
  1437.     for (; row && hasClass(row, "watchRow"); row = row.previousSibling)
  1438.         ++index;
  1439.     return index;
  1440. }
  1441.  
  1442. function getRowName(row)
  1443. {
  1444.     return row.firstChild.textContent;
  1445. }
  1446.  
  1447. function getRowValue(row)
  1448. {
  1449.     return row.lastChild.firstChild.repObject;
  1450. }
  1451.  
  1452. function getRowOwnerObject(row)
  1453. {
  1454.     var parentRow = getParentRow(row);
  1455.     if (parentRow)
  1456.         return getRowValue(parentRow);
  1457. }
  1458.  
  1459. function getParentRow(row)
  1460. {
  1461.     var level = parseInt(row.getAttribute("level"))-1;
  1462.     for (row = row.previousSibling; row; row = row.previousSibling)
  1463.     {
  1464.         if (parseInt(row.getAttribute("level")) == level)
  1465.             return row;
  1466.     }
  1467. }
  1468.  
  1469. function getPath(row)
  1470. {
  1471.     var name = getRowName(row);
  1472.     var path = [name];
  1473.  
  1474.     var level = parseInt(row.getAttribute("level"))-1;
  1475.     for (row = row.previousSibling; row; row = row.previousSibling)
  1476.     {
  1477.         if (parseInt(row.getAttribute("level")) == level)
  1478.         {
  1479.             var name = getRowName(row);
  1480.             path.splice(0, 0, name);
  1481.  
  1482.             --level;
  1483.         }
  1484.     }
  1485.  
  1486.     return path;
  1487. }
  1488.  
  1489. // ************************************************************************************************
  1490.  
  1491. Firebug.registerPanel(DOMMainPanel);
  1492. Firebug.registerPanel(DOMSidePanel);
  1493. Firebug.registerPanel(WatchPanel);
  1494.  
  1495. // ************************************************************************************************
  1496.  
  1497. }});
  1498.